﻿Imports System.IO
Imports System.Text
Imports System.Windows.Forms

Public Class BlueRichText
    Private richbox As RichTextBox
    Private coding As Encoding
    Private showntext As String = ""
    Private filepath As String
    Private filetypes As String = "Text File (*.txt)|*.txt" & "|Batch File (*.bat; *.cmd)|*.bat" & "|HTML File (*.html; *.htm)|*.html" &
            "|RTF File (*.rtf)|*.rtf" & "|All Files (*.*)|*.*"
    Private pagenum, total As Integer

    Sub New(richtextbox As RichTextBox)
        richbox = richtextbox
        TextPath = ""
        TextEncoding = Encoding.UTF8
    End Sub

    Sub New(richtextbox As RichTextBox, richtext As String, richpath As String, textcoding As Encoding)
        richbox = richtextbox
        Text = richtext
        TextPath = richpath
        TextEncoding = textcoding
    End Sub

    Property TextEncoding As Encoding
        Get
            Return coding
        End Get
        Private Set(value As Encoding)
            coding = value
        End Set
    End Property

    Property Text As String
        Get
            Return showntext
        End Get
        Private Set(value As String)
            showntext = value
        End Set
    End Property

    Property TextPath As String
        Get
            Return filepath
        End Get
        Private Set(value As String)
            filepath = value
        End Set
    End Property

    Function LoadFile() As Boolean
        If OpenDialog() Then
            LoadFileFrom(TextPath)
            Return True
        End If
        Return False
    End Function

    Sub LoadFileFrom(lopath As String)
        TextPath = lopath
        richbox.Clear()
        If Path.GetExtension(TextPath) = ".rtf" Then
            Try
                richbox.LoadFile(TextPath, RichTextBoxStreamType.RichText)
                Text = richbox.Rtf
                TextEncoding = Encoding.Default
            Catch
                setText()
            End Try
        Else
            setText()
        End If
    End Sub

    Private Sub setText()
        TextEncoding = GetFileEncoding(TextPath)
        Dim fs As FileStream = File.Open(TextPath, FileMode.OpenOrCreate, FileAccess.Read, FileShare.ReadWrite)
        Dim sr As New StreamReader(fs, TextEncoding)
        richbox.Text = sr.ReadToEnd
        Text = richbox.Rtf
        sr.Close()
        fs.Close()
    End Sub

    Function SaveText() As Boolean
        If TextPath <> "" Then
            Text = richbox.Rtf
            If Path.GetExtension(TextPath) = ".rtf" Then
                richbox.SaveFile(TextPath, RichTextBoxStreamType.RichText)
            Else
                richbox.SelectAll()
                If richbox.SelectionFont Is Nothing Or richbox.Rtf.Contains("{\pict") Then
                    If MessageBox.Show(My.Resources.rt_format_text, My.Resources.rt_convert_title, MessageBoxButtons.YesNoCancel) <> DialogResult.Yes Then
                        Return False
                    End If
                End If
                tofile()
                richbox.SelectionLength = 0
            End If
            Return True
        Else
            Return False
        End If
    End Function

    Function SaveTextAs() As Boolean
        If SaveDialog() Then
            Text = richbox.Rtf
            Select Case Path.GetExtension(TextPath)
                Case ".rtf"
                    TextEncoding = Encoding.Default
                    richbox.SaveFile(TextPath, RichTextBoxStreamType.RichText)
                Case ".cmd", "bat"
                    richbox.SelectAll()
                    If richbox.SelectionFont Is Nothing Or richbox.Rtf.Contains("{\pict") Then
                        If MessageBox.Show(My.Resources.rt_format_text, My.Resources.rt_convert_title, MessageBoxButtons.YesNoCancel) <> DialogResult.Yes Then
                            Return False
                        End If
                    End If
                    TextEncoding = Encoding.Default
                    tofile()
                    richbox.SelectionLength = 0
                Case Else
                    richbox.SelectAll()
                    If richbox.SelectionFont Is Nothing Or richbox.Rtf.Contains("{\pict") Then
                        If MessageBox.Show(My.Resources.rt_format_text, My.Resources.rt_convert_title, MessageBoxButtons.YesNoCancel) <> DialogResult.Yes Then
                            Return False
                        End If
                    End If
                    TextEncoding = Encoding.UTF8
                    tofile()
                    richbox.SelectionLength = 0
            End Select
            Return True
        End If
        Return False
    End Function

    Private Sub tofile()
        Dim fs As FileStream = File.Open(TextPath, FileMode.OpenOrCreate, FileAccess.Write, FileShare.ReadWrite)
        Dim sr As New StreamWriter(fs, TextEncoding)
        For Each line As String In richbox.Lines
            sr.WriteLine(line)
        Next
        sr.Close()
        fs.Close()
    End Sub

    Function PrintText() As Boolean
        Dim printsettings As Printing.PrinterSettings = Nothing
        If PrintDialog(printsettings) Then
            Dim printdoc As New Printing.PrintDocument
            printdoc.PrinterSettings = printsettings
            printdoc.DocumentName = Path.GetFileName(TextPath)
            printdoc.DefaultPageSettings.Margins = New Printing.Margins(0, 0, 0, 0)
            printdoc.OriginAtMargins = True
            pagenum = 0
            total = 0
            AddHandler printdoc.PrintPage, AddressOf printpage
            printdoc.Print()
            Return True
        Else
            Return False
        End If
    End Function

    Private Sub printpage(sender As Object, e As Printing.PrintPageEventArgs)
        Dim characters, lines As Integer
        Dim printfont As New Font("Lucida Console", 9)
        Dim titlefont As New Font("Arial Bold", 9)
        Dim printformat As New StringFormat(StringFormatFlags.LineLimit)
        Dim titleformat As New StringFormat
        titleformat.Alignment = StringAlignment.Far
        Dim printsize As New Size(e.PageBounds.Width - 140, e.PageBounds.Height - 140)
        Dim printarea As New Rectangle(90, 70, printsize.Width, printsize.Height)
        Dim printtext As String = richbox.Text.Substring(total)
        Dim size As SizeF = e.Graphics.MeasureString(printtext, printfont, printsize, printformat, characters, lines)
        pagenum += 1
        total += characters
        e.Graphics.DrawString(Path.GetFileName(TextPath), titlefont, Brushes.Black, printsize.Width + 90, 30, titleformat)
        e.Graphics.DrawString(printtext, printfont, Brushes.Black, printarea, printformat)
        Dim footer As String = My.Resources.page & " " & pagenum.ToString
        e.Graphics.DrawString(footer, titlefont, Brushes.Black, printsize.Width + 90, e.PageBounds.Height - 65, titleformat)
        If total < MainForm.txtMain.Text.Length Then
            e.HasMorePages = True
        Else
            e.HasMorePages = False
        End If
    End Sub

    Sub Clear()
        richbox.Clear()
        Text = ""
        TextEncoding = Encoding.UTF8
        TextPath = ""
    End Sub

    Private Function PrintDialog(ByRef settings As Printing.PrinterSettings) As Boolean
        Dim pfd As New PrintDialog()
        If pfd.ShowDialog = DialogResult.OK Then
            settings = pfd.PrinterSettings
            Return True
        Else
            Return False
        End If
    End Function

    Private Function SaveDialog() As Boolean
        Dim sfd As New SaveFileDialog()
        sfd.Filter = filetypes
        sfd.Title = My.Resources.rt_save
        sfd.FileName = "Unnamed"
        sfd.FilterIndex = 1
        If sfd.ShowDialog = DialogResult.OK Then
            TextPath = sfd.FileName
            Return True
        Else
            Return False
        End If
    End Function

    Private Function OpenDialog() As Boolean
        Dim ofd As New OpenFileDialog()
        ofd.Filter = filetypes
        ofd.Title = My.Resources.rt_open
        ofd.FileName = ""
        ofd.FilterIndex = 5
        If ofd.ShowDialog = DialogResult.OK Then
            TextPath = ofd.FileName
            Return True
        Else
            Return False
        End If
    End Function

    Function ConvertEncoding(newcoding As Encoding, frombyte As Boolean) As Boolean
        TextEncoding = newcoding
        If frombyte Then
            If fromFile(newcoding) Then
                Return True
            Else
                Return False
            End If
        End If
        Return True
    End Function

    Private Function fromFile(newcoding As Encoding) As Boolean
        If TextPath <> "" Then
            If MessageBox.Show(My.Resources.rt_convert_text, My.Resources.rt_convert_title, MessageBoxButtons.YesNoCancel) =
                DialogResult.Yes Then
                Dim fs As FileStream = File.Open(TextPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)
                Dim sr As New StreamReader(fs)
                richbox.Text = sr.ReadToEnd
                Text = richbox.Rtf
                sr.Close()
                fs.Close()
                Return True
            Else
                Return False
            End If
        Else
            Return False
        End If
    End Function

    Shared Function GetFileEncoding(path As String) As Encoding
        Dim codeset As Encoding = Nothing
        If path.EndsWith(".bat") Or path.EndsWith(".cmd") Then
            codeset = Encoding.Default
            Return codeset
        End If
        Dim bytefile() As Byte
        bytefile = File.ReadAllBytes(path)
        For Each codetype In Encoding.GetEncodings
            Dim preamble() As Byte = codetype.GetEncoding.GetPreamble
            If bytefile.Length > 0 And preamble.Length < bytefile.Length Then
                Dim matching As Boolean = False
                For i = 0 To preamble.Length - 1
                    If bytefile(i) = preamble(i) Then
                        matching = True
                    Else
                        matching = False
                    End If
                Next
                If matching = True Then
                    Dim possible As Boolean = TryEncoding(path, codetype.GetEncoding)
                    If possible Then
                        codeset = codetype.GetEncoding
                    End If
                End If
            End If
        Next
        If codeset Is Nothing Then
            codeset = Encoding.UTF8
            Dim utf8check As Boolean = TryEncoding(path, codeset)
            If utf8check = False Then
                codeset = Encoding.Unicode
                Dim utf16lecheck As Boolean = TryUTF16(path, codeset, False)
                If utf16lecheck = False Then
                    codeset = Encoding.BigEndianUnicode
                    Dim utf16becheck As Boolean = TryUTF16(path, codeset, True)
                    If utf16becheck = False Then
                        codeset = Encoding.Default
                    End If
                End If
            End If
        End If
        Return codeset
    End Function

    Private Shared Function TryEncoding(path As String, encoding As Encoding) As Boolean
        Dim tryfile As New StreamReader(path, encoding, False)
        Dim showtext As String = tryfile.ReadToEnd
        tryfile.Close()
        If showtext.IndexOf(ChrW(65533)) <> -1 Then
            Return False
        Else
            Return True
        End If
    End Function

    Private Shared Function TryUTF16(path As String, encoding As Encoding, be As Boolean) As Boolean
        Dim tryfile() As Byte = File.ReadAllBytes(path)
        Dim empty As Byte = 0
        Dim includes As Integer = Array.IndexOf(tryfile, empty)
        If includes <> -1 Then
            If includes Mod 2 = 0 Then
                If be Then Return True Else Return False
            Else
                If be Then Return False Else Return True
            End If
        Else
            Return False
        End If
    End Function

End Class
